#include "INetworkSnapshotDumper.h"

struct NetworkSnapshotGraphVizDumper: INetworkSnapshotDumper {
  virtual void dump(std::ostream& out, const NetworkSnapshotPtr snapshot) {
    
    size_t networkCounter = 0;
    std::string tab = "  ";
    std::map<SubNet::Id, size_t> subNetIdMap; //tp store id gViz
    out << "graph Networks {" << std::endl;
    Networks::const_iterator netIt, netEnd;

    netIt = snapshot->getNetworks().begin();
    netEnd = snapshot->getNetworks().end();

    for(; netIt != netEnd; ++netIt) {
      size_t routerCounter = 0;
      size_t subNetCounter = 0;
      
      SubNetMap::const_iterator subNetIt, subNetItEnd;
      subNetIt = netIt->getSubNetworks().begin();
      subNetItEnd = netIt->getSubNetworks().end();

      NetRouters::const_iterator routeIt, routeEnd;
      routeIt = netIt->getRouters().begin();
      routeEnd = netIt->getRouters().end();
      out << tab;
      out << "subgraph network" << networkCounter 
          << " {" << std::endl;
      out << tab;
      out << "label=\"" << netIt->getId() << "\";"
          << std::endl;
      for(; subNetIt != subNetItEnd; ++subNetIt) {
        size_t nodeCounter = 0;
        NetNodes::const_iterator nodeIt, nodeItEnd;
        nodeIt  = subNetIt->second.getNodes().begin();
        nodeItEnd = subNetIt->second.getNodes().end();
        
        out << tab;
        out << "subgraph cluster_net" << networkCounter
          << "subnet" << subNetCounter
          << " { " << std::endl;
        out << tab;
        out << "node [style = filled];" << std::endl;
        out << tab;
        out << "rankdir=\"TB\"" << std::endl;
        out << tab;
        out << "color = black;" << std::endl;
        out << tab;
        out << "label = \"Subnetwork "
          << subNetIt->second.getId() << "\";" << std::endl;

        for(; nodeIt != nodeItEnd; ++nodeIt) {
          out << tab;
          out << "net"    << networkCounter
            << "subnet" << subNetCounter
            << "node"   << nodeCounter;
          if (nodeIt->getName().size()) {
            out << " [label=\"" << nodeIt->getName() <<"\"];";
          } else {
            out << " [label=\"" << nodeIt->getAddress() <<"\"];";
          }
          out << std::endl;
          ++nodeCounter;
        }

        out << tab;
        out << "}" << std::endl;
        subNetIdMap[subNetIt->second.getId()] = subNetCounter;
        ++subNetCounter;
      }

      for(; routeIt != routeEnd; ++routeIt) {
        NetInterfaces::const_iterator interfIt, interfItEnd;
        size_t interfCounter = 0;
        interfIt = routeIt->getInterfaces().begin();
        interfItEnd = routeIt->getInterfaces().end();

        out << tab;
        out << "subgraph cluster_net" << networkCounter
            << "_router" << routerCounter << " { " << std::endl;
        out << tab;
        out << "node [style = filled, color=white];" << std::endl;
        out << tab;
        out << "color = lightgrey;" << std::endl;
        out << tab;
        out << "style = filled;" << std::endl;
        out << tab;
        out << "label = \"Router: ";
        /* monkey code but there is nothing standard for it in C++ */
        std::string routerDescr = routeIt->getDescription();
        while(routerDescr.size()) {
          size_t lineLen = 0;
          if (routerDescr.size() > 30) {
            lineLen = 30;
            while(routerDescr[lineLen++] != ' ');
          } else {
            lineLen = routerDescr.size();
          }
          for(size_t i = 0; i < lineLen; ++i) {
            if ((routerDescr[i] >= 'a' && routerDescr[i] <= 'z') ||
                (routerDescr[i] >= 'A' && routerDescr[i] <= 'Z') ||
                (routerDescr[i] >= '0' && routerDescr[i] <= '9') ||
                 routerDescr[i] == '(' || routerDescr[i] == ')'  ||
                 routerDescr[i] == '-') {
              out << routerDescr[i];
            } else { out << ' '; }
          }
          out << "\\n\\\n";
          routerDescr.erase(0, lineLen);
        }
        out << "\";" << std::endl;
        
        for(; interfIt != interfItEnd; ++interfIt) {
          out << tab;
          out << "net"<< networkCounter
            << "rt" << routerCounter 
            << "if" << interfCounter 
            << " [label=\"" << interfIt->getAddress()
            << "\\nName:" << interfIt->getName()
            << "\\nInBytes: " << interfIt->getInBytes()
            << "\\nOutBytes: " << interfIt->getOutBytes()
            << "\"];"
            << std::endl; 
          ++interfCounter;
        }
        out << tab;
        out << "}" << std::endl;

        for(interfIt = routeIt->getInterfaces().begin();
            interfIt != interfItEnd; ++interfIt) {
          //connect router and subnet
          out << tab
            << "cluster_net" << networkCounter
            << "_router" << routerCounter
            << " -- " 
            << "cluster_net" << networkCounter
            << "subnet" << subNetIdMap[interfIt->getSubNetId()]
            << ";" << std::endl;
        }

        ++routerCounter;
      }
      ++networkCounter;
    }
    out << "}" << std::endl;
    out << "}" << std::endl;
  }
};
